import { ArrayType, EPSILON } from "./common";
/**
 * 3*3矩阵
 * @class
 * @author jsbrml
 */
class Mat3 extends ArrayType {
    /**
     * 构造函数。
     * @constructor
     * @example
     * let mat=new Mat3([2,0,0,0,2,0,0,0,1]);
     */
    constructor(element = [1, 0, 0, 0, 1, 0, 0, 0, 1]) {
        super(element);
    }

    /**
     * 3阶矩阵的克隆。
     * @method
     * @public
     * @example
     * let mat3=new Mat3();
     * let clone=mat3.clone();
     * @returns {Mat3} Mat3。
     */
    clone() {
        return new Mat3(this);
    }
    /**
     * 3阶矩阵的拷贝。
     * @method
     * @public
     * @example
     * let mat3=new Mat3();
     * let copy=new Mat3();
     * copy.copy(mat3);
     * @returns {Mat3} Mat3。
     */
    copy(a) {
        this[0] = a[0];
        this[1] = a[1];
        this[2] = a[2];
        this[3] = a[3];
        this[4] = a[4];
        this[5] = a[5];
        this[6] = a[6];
        this[7] = a[7];
        this[8] = a[8];
        return this;
    }
    /**
     * 3阶矩阵的等式判断。
     * @method
     * @public
     * @example
     * let mat3=new Mat3();
     * let mat32=new Mat3();
     * let isEqual=mat3.equal(mat32);//true
     * @returns {Boolean} 是否相等。
     */
    equal(v) {
        let a0 = this[0],
            a1 = this[1],
            a2 = this[2],
            a3 = this[3],
            a4 = this[4],
            a5 = this[5],
            a6 = this[6],
            a7 = this[7],
            a8 = this[8];
        let b0 = v[0],
            b1 = v[1],
            b2 = v[2],
            b3 = v[3],
            b4 = v[4],
            b5 = v[5],
            b6 = v[6],
            b7 = v[7],
            b8 = v[8];
        return (
            Math.abs(a0 - b0) <=
            EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
            Math.abs(a1 - b1) <=
            EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
            Math.abs(a2 - b2) <=
            EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
            Math.abs(a3 - b3) <=
            EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
            Math.abs(a4 - b4) <=
            EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
            Math.abs(a5 - b5) <=
            EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
            Math.abs(a6 - b6) <=
            EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
            Math.abs(a7 - b7) <=
            EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
            Math.abs(a8 - b8) <=
            EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8))
        );
    }
    /**
     * 将矩阵设置为单位矩阵。
     * @method
     * @public
     * @example
     * let mat=new Mat3([3,0,0,0,1,0,0,0,1]);
     * mat.identity();
     * @returns {Mat3} 单位矩阵
     */
    identity() {
        this[0] = 1;
        this[1] = 0;
        this[2] = 0;
        this[3] = 0;
        this[4] = 1;
        this[5] = 0;
        this[6] = 0;
        this[7] = 0;
        this[8] = 1;
        return this;
    }
    /**
     * 对3阶矩阵进行转置变换。
     * @method
     * @public
     * @example
     * let mat=new Mat3([3,0,0,0,1,0,0,0,1]);
     * mat.transpose();
     * @returns {Mat3} 转置矩阵
     */
    transpose() {
        // If we are transposing ourselves we can skip a few steps but have to cache some values
        let a = this.clone();
        this[0] = a[0];
        this[1] = a[3];
        this[2] = a[6];
        this[3] = a[1];
        this[4] = a[4];
        this[5] = a[7];
        this[6] = a[2];
        this[7] = a[5];
        this[8] = a[8];
        a = null;
        return this;
    }
    /**
     * 对3阶矩阵进行逆变换。
     * @method
     * @public
     * @example
     * let mat=new Mat3([3,0,0,0,1,0,0,0,1]);
     * mat.invert();
     * @returns {Mat3} 逆矩阵
     */
    invert() {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2];
        let a10 = this[3],
            a11 = this[4],
            a12 = this[5];
        let a20 = this[6],
            a21 = this[7],
            a22 = this[8];

        let b01 = a22 * a11 - a12 * a21;
        let b11 = -a22 * a10 + a12 * a20;
        let b21 = a21 * a10 - a11 * a20;

        // Calculate the determinant
        let det = a00 * b01 + a01 * b11 + a02 * b21;

        if (!det) {
            return null;
        }
        det = 1.0 / det;

        this[0] = b01 * det;
        this[1] = (-a22 * a01 + a02 * a21) * det;
        this[2] = (a12 * a01 - a02 * a11) * det;
        this[3] = b11 * det;
        this[4] = (a22 * a00 - a02 * a20) * det;
        this[5] = (-a12 * a00 + a02 * a10) * det;
        this[6] = b21 * det;
        this[7] = (-a21 * a00 + a01 * a20) * det;
        this[8] = (a11 * a00 - a01 * a10) * det;
        return this;
    }
    /**
     * 计算3阶矩阵的伴随阵。
     * @method
     * @public
     * @example
     * let mat=new Mat3([3,0,0,0,1,0,0,0,1]);
     * mat.adjoint();
     * @returns {Mat3} 伴随阵
     */
    adjoint() {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2];
        let a10 = this[3],
            a11 = this[4],
            a12 = this[5];
        let a20 = this[6],
            a21 = this[7],
            a22 = this[8];

        this[0] = a11 * a22 - a12 * a21;
        this[1] = a02 * a21 - a01 * a22;
        this[2] = a01 * a12 - a02 * a11;
        this[3] = a12 * a20 - a10 * a22;
        this[4] = a00 * a22 - a02 * a20;
        this[5] = a02 * a10 - a00 * a12;
        this[6] = a10 * a21 - a11 * a20;
        this[7] = a01 * a20 - a00 * a21;
        this[8] = a00 * a11 - a01 * a10;
        return this;
    }
    /**
     * 计算3阶矩阵的行列式。
     * @method
     * @public
     * @example
     * let mat=new Mat3([3,0,0,0,1,0,0,0,1]);
     * mat.determinant();
     * @returns {Number} 返回矩阵det
     */
    determinant() {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2];
        let a10 = this[3],
            a11 = this[4],
            a12 = this[5];
        let a20 = this[6],
            a21 = this[7],
            a22 = this[8];
        return (
            a00 * (a22 * a11 - a12 * a21) +
            a01 * (-a22 * a10 + a12 * a20) +
            a02 * (a21 * a10 - a11 * a20)
        );
    }
    /**
     * 矩阵乘法
     * @method
     * @public
     * @param {Mat3} v 另外一个Mat3矩阵。
     * @example
     * let a=new Mat3([3,0,0,0,1,0,0,0,1]);
     * let b=new Mat3([2,0,0,0,1,0,0,0,1]);
     * a.multiply(b);//a变为乘后矩阵。
     * //如果保留a,则可以先克隆后乘。
     * let c=a.clone();
     * c.multiply(b);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    multiply(b) {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2];
        let a10 = this[3],
            a11 = this[4],
            a12 = this[5];
        let a20 = this[6],
            a21 = this[7],
            a22 = this[8];

        let b00 = b[0],
            b01 = b[1],
            b02 = b[2];
        let b10 = b[3],
            b11 = b[4],
            b12 = b[5];
        let b20 = b[6],
            b21 = b[7],
            b22 = b[8];
        this[0] = b00 * a00 + b01 * a10 + b02 * a20;
        this[1] = b00 * a01 + b01 * a11 + b02 * a21;
        this[2] = b00 * a02 + b01 * a12 + b02 * a22;

        this[3] = b10 * a00 + b11 * a10 + b12 * a20;
        this[4] = b10 * a01 + b11 * a11 + b12 * a21;
        this[5] = b10 * a02 + b11 * a12 + b12 * a22;

        this[6] = b20 * a00 + b21 * a10 + b22 * a20;
        this[7] = b20 * a01 + b21 * a11 + b22 * a21;
        this[8] = b20 * a02 + b21 * a12 + b22 * a22;
        return this;
    }
    /**
     * 矩阵左乘
     * @method
     * @public
     * @param {Mat3} v 另外一个Mat3矩阵。
     * @example
     * let a=new Mat3([3,0,0,0,1,0,0,0,1]);
     * let b=new Mat3([2,0,0,0,1,0,0,0,1]);
     * a.premultiply(b);//a变为乘后矩阵。
     * //如果保留a,则可以先克隆后乘。
     * let c=a.clone();
     * c.premultiply(b);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    premultiply(b) {
        let a00 = b[0],
            a01 = b[1],
            a02 = b[2];
        let a10 = b[3],
            a11 = b[4],
            a12 = b[5];
        let a20 = b[6],
            a21 = b[7],
            a22 = b[8];

        let b00 = this[0],
            b01 = this[1],
            b02 = this[2];
        let b10 = this[3],
            b11 = this[4],
            b12 = this[5];
        let b20 = this[6],
            b21 = this[7],
            b22 = this[8];
        this[0] = b00 * a00 + b01 * a10 + b02 * a20;
        this[1] = b00 * a01 + b01 * a11 + b02 * a21;
        this[2] = b00 * a02 + b01 * a12 + b02 * a22;

        this[3] = b10 * a00 + b11 * a10 + b12 * a20;
        this[4] = b10 * a01 + b11 * a11 + b12 * a21;
        this[5] = b10 * a02 + b11 * a12 + b12 * a22;

        this[6] = b20 * a00 + b21 * a10 + b22 * a20;
        this[7] = b20 * a01 + b21 * a11 + b22 * a21;
        this[8] = b20 * a02 + b21 * a12 + b22 * a22;
        return this;
    }
    /**
     * 矩阵缩放变换
     * @method
     * @public
     * @param {Vec2|Array} v 缩放的2维向量或数组。
     * @example
     * let a=new Mat3([3,0,0,0,1,0,0,0,1]);
     * a.scale([2,2]);
     * //或
     * a.scale(new Vec2(2,2));
     * @returns {Mat3} 返回一个新的矩阵。
     */
    scale(v) {
        this[0] *= v[0];
        this[1] *= v[0];
        this[2] *= v[0];

        this[3] *= v[1];
        this[4] *= v[1];
        this[5] *= v[1];
        return this;
    }
    /**
     * 矩阵旋转变换
     * @method
     * @public
     * @param {Number} rad 旋转的弧度。
     * @example
     * let a=new Mat3([3,0,0,0,1,0,0,0,1]);
     * a.rotate(Math.PI/3);//旋转60°
     * @returns {Mat3} 返回一个新的矩阵。
     */
    rotate(rad) {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2],
            a10 = this[3],
            a11 = this[4],
            a12 = this[5],
            a20 = this[6],
            a21 = this[7],
            a22 = this[8],
            s = Math.sin(rad),
            c = Math.cos(rad);

        this[0] = c * a00 + s * a10;
        this[1] = c * a01 + s * a11;
        this[2] = c * a02 + s * a12;

        this[3] = c * a10 - s * a00;
        this[4] = c * a11 - s * a01;
        this[5] = c * a12 - s * a02;

        this[6] = a20;
        this[7] = a21;
        this[8] = a22;
        return this;
    }
    /**
     * 矩阵平移变换
     * @method
     * @public
     * @param {Vec2|Array} v 平移量。
     * @example
     * let a=new Mat3([3,0,0,0,1,0,0,0,1]);
     * a.translate([2,3]);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    translate(v) {
        let a00 = this[0],
            a01 = this[1],
            a02 = this[2],
            a10 = this[3],
            a11 = this[4],
            a12 = this[5],
            a20 = this[6],
            a21 = this[7],
            a22 = this[8],
            x = v[0],
            y = v[1];
        this[0] = a00;
        this[1] = a01;
        this[2] = a02;
        this[3] = a10;
        this[4] = a11;
        this[5] = a12;
        this[6] = x * a00 + y * a10 + a20;
        this[7] = x * a01 + y * a11 + a21;
        this[8] = x * a02 + y * a12 + a22;
        return this;
    }
    /**
     * 从一个4阶矩阵构建3阶矩阵。
     * @method
     * @public
     * @param {Mat4} mat4 4阶矩阵
     * @example
     * let a=new Mat4();
     * let mat4=new Mat4([2,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
     * a.fromMat4(mat4);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    fromMat4(mat4) {
        this[0] = mat4[0];
        this[1] = mat4[1];
        this[2] = mat4[2];
        this[3] = mat4[4];
        this[4] = mat4[5];
        this[5] = mat4[6];
        this[6] = mat4[8];
        this[7] = mat4[9];
        this[8] = mat4[10];
        return this;
    }
    /**
     * 从一个四元数构建3阶矩阵。
     * @method
     * @public
     * @param {Quat} quat 四元数
     * @example
     * let a=new Mat3();
     * let quat=new Quat([0.2,0.4,-0.2,1]);
     * a.fromQuat(quat);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    fromQuat(quat) {
        let x = quat[0],
            y = quat[1],
            z = quat[2],
            w = quat[3];
        let x2 = x + x;
        let y2 = y + y;
        let z2 = z + z;

        let xx = x * x2;
        let yx = y * x2;
        let yy = y * y2;
        let zx = z * x2;
        let zy = z * y2;
        let zz = z * z2;
        let wx = w * x2;
        let wy = w * y2;
        let wz = w * z2;

        this[0] = 1 - yy - zz;
        this[3] = yx - wz;
        this[6] = zx + wy;

        this[1] = yx + wz;
        this[4] = 1 - xx - zz;
        this[7] = zy - wx;

        this[2] = zx - wy;
        this[5] = zy + wx;
        this[8] = 1 - xx - yy;
        return this
    }
    /**
     * 从一个平移量构建3阶矩阵。
     * @method
     * @public
     * @param {Vec2|Array} v 平移量
     * @example
     * let a=new Mat3();
     * a.fromTranslation([2,3]);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    fromTranslation(v) {
        this[0] = 1;
        this[1] = 0;
        this[2] = 0;
        this[3] = 0;
        this[4] = 1;
        this[5] = 0;
        this[6] = v[0];
        this[7] = v[1];
        this[8] = 1;
        return this;
    }
    /**
     * 从一个缩放量构建3阶矩阵。
     * @method
     * @public
     * @param {Vec2|Array} v 平移量
     * @example
     * let a=new Mat3();
     * a.fromScaling([2,3]);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    fromScaling(v) {
        this[0] = v[0];
        this[1] = 0;
        this[2] = 0;

        this[3] = 0;
        this[4] = v[1];
        this[5] = 0;

        this[6] = 0;
        this[7] = 0;
        this[8] = 1;
        return this;
    }
    /**
     * 从一个旋转弧度构建3阶矩阵。
     * @method
     * @public
     * @param {Number} rad 旋转弧度
     * @example
     * let a=new Mat3();
     * a.fromRotation(Math.PI/3);
     * @returns {Mat3} 返回一个新的矩阵。
     */
    fromRotation(rad) {
        const s = Math.sin(rad);
        const c = Math.cos(rad);

        this[0] = c;
        this[1] = s;
        this[2] = 0;

        this[3] = -s;
        this[4] = c;
        this[5] = 0;

        this[6] = 0;
        this[7] = 0;
        this[8] = 1;
        return this;
    }
    /**
     * 从一个4阶矩阵获取3阶法向量矩阵。
     * @method
     * @public
     * @param {Mat4} a 4阶矩阵
     * @example
     * let mat4=new Mat4([2,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
     * let normal=new Mat3();
     * normal.normalFromMat4(mat4);
     * @returns {Mat3} 返回一个新的3阶法向量矩阵。
     */
    normalFromMat4(a) {
        let a00 = a[0],
            a01 = a[1],
            a02 = a[2],
            a03 = a[3];
        let a10 = a[4],
            a11 = a[5],
            a12 = a[6],
            a13 = a[7];
        let a20 = a[8],
            a21 = a[9],
            a22 = a[10],
            a23 = a[11];
        let a30 = a[12],
            a31 = a[13],
            a32 = a[14],
            a33 = a[15];

        let b00 = a00 * a11 - a01 * a10;
        let b01 = a00 * a12 - a02 * a10;
        let b02 = a00 * a13 - a03 * a10;
        let b03 = a01 * a12 - a02 * a11;
        let b04 = a01 * a13 - a03 * a11;
        let b05 = a02 * a13 - a03 * a12;
        let b06 = a20 * a31 - a21 * a30;
        let b07 = a20 * a32 - a22 * a30;
        let b08 = a20 * a33 - a23 * a30;
        let b09 = a21 * a32 - a22 * a31;
        let b10 = a21 * a33 - a23 * a31;
        let b11 = a22 * a33 - a23 * a32;

        // Calculate the determinant
        let det =
            b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

        if (!det) {
            return null;
        }
        det = 1.0 / det;

        this[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
        this[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
        this[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;

        this[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
        this[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
        this[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;

        this[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
        this[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
        this[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;

        return this;
    }
    /**
     * 构建投影矩阵
     * @method
     * @public
     * @param {Number} width 
     * @param {Number} height 
     * @example
     * let a=new Mat3();
     * a.projection(1,10);
     * @returns {Mat3} 一个投影矩阵
     */
    projection(width, height) {
        this[0] = 2 / width;
        this[1] = 0;
        this[2] = 0;
        this[3] = 0;
        this[4] = -2 / height;
        this[5] = 0;
        this[6] = -1;
        this[7] = 1;
        this[8] = 1;
        return this;
      }

    /**
     * 3阶矩阵转换为字符串。
     * @method
     * @public
     * @example
     * let a=new Mat3();
     * a.toString()//"Mat3(1,0,0,0,1,0,0,0,1)"
     * @returns {String} 返回矩阵的字符串形式。
     */
    toString() {
        return `Mat3(${this.join(',')})`
    }
}
export { Mat3 }